#include <windows.h>
#include <stdio.h>
#include "../IExample/IExample.h"

void main()
{
    HRESULT hr;
    IExample *example;

    hr = CoInitialize(NULL);
    if (FAILED(hr))
    {
        fprintf(stderr, "CoInitialize failed with error code: %x\n", hr);
        return;
    }

    hr = CoCreateInstance(CLSID_IExample, NULL, CLSCTX_INPROC_SERVER, IID_IExample, (LPVOID *)&example);
    if (FAILED(hr))
    {
        fprintf(stderr, "CoCreateInstance failed with error code: %x\n", hr);
        CoUninitialize();
        return;
    }

    BSTR str = SysAllocString(L"hello, world!");
    example->SetString(str);
    SysFreeString(str);

    BSTR str2 = NULL;
    example->GetString(&str2);
    wprintf(L"%s\n", str2);
    SysFreeString(str2);

    ////////////////////////////////////////////////////////////////////////////////

    IDispatch *PortsColl = NULL;
    hr = example->GetPorts(&PortsColl);
    if (FAILED(hr))
    {
        fprintf(stderr, "GetPorts failed with error code: %x\n", hr);
        example->Release();
        CoUninitialize();
        return;
    }

    DISPID id;
    OLECHAR *names = L"Count";
    hr = PortsColl->GetIDsOfNames(IID_NULL, &names, 1, LOCALE_USER_DEFAULT, &id);
    if (FAILED(hr))
    {
        fprintf(stderr, "GetIDsOfNames failed with error code: %x\n", hr);
        PortsColl->Release();
        example->Release();
        CoUninitialize();
        return;
    }

    DISPPARAMS dspp;
    ZeroMemory(&dspp, sizeof(DISPPARAMS));

    VARIANT ret;
    VariantInit(&ret);

    hr = PortsColl->Invoke(id, IID_NULL, LOCALE_USER_DEFAULT,
        DISPATCH_METHOD | DISPATCH_PROPERTYGET, &dspp, &ret, NULL, NULL);
    if (FAILED(hr))
    {
        fprintf(stderr, "Invoke failed with error code: %x\n", hr);
        PortsColl->Release();
        example->Release();
        CoUninitialize();
        return;
    }

    if (ret.vt != VT_I4)
    {
        hr = VariantChangeType(&ret, &ret, 0, VT_I4);
        if (FAILED(hr))
        {
            fprintf(stderr, "VariantChangeType failed with error code: %x\n", hr);
            PortsColl->Release();
            example->Release();
            CoUninitialize();
            return;
        }
    }

    LONG count = ret.lVal;
    VariantClear(&ret);

    VARIANT args[1];
    VariantInit(&args[0]);
    args[0].vt = VT_I4;

    ZeroMemory(&dspp, sizeof(DISPPARAMS));
    dspp.cArgs = 1;
    dspp.rgvarg = args;

    for (LONG i = 0; i < count; ++i)
    {
        args[0].lVal = i;
        hr = PortsColl->Invoke(DISPID_VALUE, IID_NULL, LOCALE_USER_DEFAULT,
            DISPATCH_METHOD | DISPATCH_PROPERTYGET, &dspp, &ret, NULL, NULL);
        if (FAILED(hr))
        {
            fprintf(stderr, "Invoke failed with error code: %x\n", hr);
            PortsColl->Release();
            example->Release();
            CoUninitialize();
            return;
        }

        wprintf(L"%s\n", ret.bstrVal);
        VariantClear(&ret);
    }

    PortsColl->Release();

    ////////////////////////////////////////////////////////////////////////////////

    IDispatch *NetCardsColl = NULL;
    hr = example->GetNetCards(&NetCardsColl);
    example->Release();
    if (FAILED(hr))
    {
        fprintf(stderr, "GetNetCards failed with error code: %x\n", hr);
        CoUninitialize();
        return;
    }

    ZeroMemory(&dspp, sizeof(DISPPARAMS));

    hr = NetCardsColl->Invoke(DISPID_NEWENUM, IID_NULL, LOCALE_USER_DEFAULT,
        DISPATCH_METHOD | DISPATCH_PROPERTYGET, &dspp, &ret, NULL, NULL);
    NetCardsColl->Release();
    if (FAILED(hr))
    {
        fprintf(stderr, "Invoke failed with error code: %x\n", hr);
        CoUninitialize();
        return;
    }

    if (ret.vt != VT_UNKNOWN)
    {
        fprintf(stderr, "Invalid return type\n");
        VariantClear(&ret);
        CoUninitialize();
        return;
    }

    IEnumVARIANT *pEnum = NULL;
    hr = ret.punkVal->QueryInterface(IID_IEnumVARIANT, (void **)&pEnum);
    VariantClear(&ret);
    if (FAILED(hr))
    {
        fprintf(stderr, "QueryInterface failed with error code: %x\n", hr);
        CoUninitialize();
        return;
    }

    VARIANT items[10];
    for (int i = 0; i < 10; i++) VariantInit(&items[i]);

    ULONG num;
    hr = pEnum->Next(10, items, &num);
    pEnum->Release();
    if (FAILED(hr))
    {
        fprintf(stderr, "Next failed with error code: %x\n", hr);
        CoUninitialize();
        return;
    }

    for (ULONG i = 0; i < num; ++i)
    {
        if (items[i].vt != VT_DISPATCH)
        {
            VariantClear(&items[i]);
            continue;
        }

        INetCard *pNetCard = NULL;
        hr = items[i].pdispVal->QueryInterface(IID_INetCard, (void **)&pNetCard);
        VariantClear(&items[i]);
        if (FAILED(hr)) continue;

        BSTR name = NULL;
        hr = pNetCard->GetName(&name);
        if (FAILED(hr))
        {
            pNetCard->Release();
            continue;
        }

        BSTR address = NULL;
        hr = pNetCard->GetAddress(&address);
        if (FAILED(hr))
        {
            SysFreeString(name);
            pNetCard->Release();
            continue;
        }

        wprintf(L"name: %s, address: %s\n", name, address);
        SysFreeString(address);
        SysFreeString(name);
        pNetCard->Release();
    }

    CoUninitialize();
}
